package technology.tabula.line;

import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader;
import org.apache.pdfbox.contentstream.PDFGraphicsStreamEngine;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.image.PDImage;
import org.apache.pdfbox.text.PDFTextStripperByArea;

import java.awt.*;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.*;
import java.util.ArrayList;

/**
 * https://stackoverflow.com/questions/55166990/pdfbox-line-rectangle-extraction
 */
public class LineCatcher extends PDFGraphicsStreamEngine {
    private static final GeneralPath linePath = new GeneralPath();
    private static ArrayList<Rectangle2D> rectList = new ArrayList<Rectangle2D>();
    private static ArrayList<Rectangle2D.Double> rectangleList = new ArrayList<>();
    private int clipWindingRule = -1;
    //private static String headerRecord = "Text|Page|x|y|width|height|space|font";
    private static String headerRecord = "Text|Page|x                 |y              |width              |height|space|font";

    public LineCatcher(PDPage page) {
        super(page);
    }

    public static void main1(String[] args) throws IOException {
        String inputFilePath = "src/main/resources/table4.pdf";
        try (PDDocument document = PDDocument.load(new File(inputFilePath))) {
            PDFTextStripperByArea stripper = new PDFTextStripperByArea();
            stripper.setSortByPosition(true);
            Rectangle rect = new Rectangle(54, 840-723-45, 54, 15);
            stripper.addRegion("class1", rect);
            PDPage firstPage = document.getPage(0);
            stripper.extractRegions(firstPage);
            System.out.println("Text in the area:" + rect);
            System.out.println(stripper.getTextForRegion("class1"));
        }
    }

    public static void main(String[] args) throws IOException {
     /*   if (args.length != 4) {
            usage();
        } else */
        {
            PDDocument document = null;
            FileOutputStream fop = null;
            File file;
            Writer osw = null;
            int numPages;
            double page_height;
            try {
                //String inputFilePath = "/home/wangjin/文档/工作簿3.pdf";
                String inputFilePath = "src/main/resources/table4.pdf";
                String outputFilePath = "result";
                document = PDDocument.load(new File(inputFilePath));
                numPages = document.getNumberOfPages();
                file = new File(outputFilePath);
                fop = new FileOutputStream(file);

                // if file doesnt exists, then create it
                if (!file.exists()) {
                    file.createNewFile();
                }

                osw = new OutputStreamWriter(fop, "UTF8");
                osw.write(headerRecord + System.lineSeparator());
                System.out.println("Line Processing numPages:" + numPages);
                for (int n = 0; n < numPages; n++) {
                    System.out.println("Line Processing page:" + n);
                    rectList = new ArrayList<Rectangle2D>();
                    PDPage page = document.getPage(n);
                    page_height = page.getCropBox().getUpperRightY();
                    LineCatcher lineCatcher = new LineCatcher(page);
                    lineCatcher.processPage(page);

                    try {
                        int pageRotation = page.getRotation();
                        PDRectangle pageCropBox = page.getCropBox();

                        for(Rectangle2D rect:rectList) {
                            String pageNum = Integer.toString(n + 1);
                            String x, y, w, h;
                            switch(pageRotation) {
                                case 0:
                                    x = Double.toString(rect.getX() - pageCropBox.getLowerLeftX());
                                    y = Double.toString(pageCropBox.getUpperRightY() - rect.getY() + rect.getHeight());
                                    w = Double.toString(rect.getWidth());
                                    h = Double.toString(rect.getHeight());
                                    break;
                                case 90:
                                    x = Double.toString(rect.getY() - pageCropBox.getLowerLeftY());
                                    y = Double.toString(rect.getX() - pageCropBox.getLowerLeftX());
                                    w = Double.toString(rect.getHeight());
                                    h = Double.toString(rect.getWidth());
                                    break;
                                case 180:
                                    x = Double.toString(pageCropBox.getUpperRightX() - rect.getX() - rect.getWidth());
                                    y = Double.toString(rect.getY() - pageCropBox.getLowerLeftY());
                                    w = Double.toString(rect.getWidth());
                                    h = Double.toString(rect.getHeight());
                                    break;
                                case 270:
                                    x = Double.toString(pageCropBox.getUpperRightY() - rect.getY() + rect.getHeight());
                                    y = Double.toString(pageCropBox.getUpperRightX() - rect.getX() - rect.getWidth());
                                    w = Double.toString(rect.getHeight());
                                    h = Double.toString(rect.getWidth());
                                    break;
                                default:
                                    throw new IOException(String.format("Unsupported page rotation %d on page %d.", pageRotation, page));
                            }
                            writeToFile(pageNum, x, y, w, h, osw);
                        }

                        System.out.println("r.x" + "\t" + "r.y" + "\t" + "r.width" + "\t" + "r.height" + "\t");
                        for (Rectangle2D.Double r : rectangleList) {
                            System.out.println(r.x + "\t" + r.y + "\t" + r.width + "\t" + r.height + "\t");
                        }
                        rectList = null;
                        page = null;
                        lineCatcher = null;
                    } catch (IOException io) {
                        throw new IOException("Failed to Parse document for line processing. Incorrect document format. Page:" + n);
                    }
                }
                ;

            } catch (IOException io) {
                throw new IOException("Failed to Parse document for line processing. Incorrect document format.");
            } finally {
                if (osw != null) {
                    osw.close();
                }
                if (document != null) {
                    document.close();
                }
            }
        }
    }

    private static void writeToFile(String pageNum, String x, String y, String w, String h, Writer osw) throws IOException {
        String c = "___^" + "|___" +
                pageNum + "|" +
                x + "|" +
                y + "|" +
                w + "|" +
                h + "|" +
                "999" + "|" +
                "marker-only";
        osw.write(c + System.lineSeparator());
    }

    @Override
    public void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) throws IOException {
        // to ensure that the path is created in the right direction, we have to create
        // it by combining single lines instead of creating a simple rectangle
        linePath.moveTo((float) p0.getX(), (float) p0.getY());
        linePath.lineTo((float) p1.getX(), (float) p1.getY());
        linePath.lineTo((float) p2.getX(), (float) p2.getY());
        linePath.lineTo((float) p3.getX(), (float) p3.getY());

        // close the subpath instead of adding the last line so that a possible set line
        // cap style isn't taken into account at the "beginning" of the rectangle
        linePath.closePath();

        System.out.println(p0 + "\t" + p1 + "\t" + p2 + "\t" + p3);
        rectangleList.add(new Rectangle2D.Double(p3.getX(), p3.getY(), p1.getX() - p3.getX(), p1.getY() - p3.getY()));
    }

    @Override
    public void drawImage(PDImage pdi) throws IOException {
    }

    @Override
    public void clip(int windingRule) throws IOException {
        // the clipping path will not be updated until the succeeding painting operator is called
        clipWindingRule = windingRule;

    }

    @Override
    public void moveTo(float x, float y) throws IOException {
        linePath.moveTo(x, y);
    }

    @Override
    public void lineTo(float x, float y) throws IOException {
        linePath.lineTo(x, y);
    }

    @Override
    public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException {
        linePath.curveTo(x1, y1, x2, y2, x3, y3);
    }

    @Override
    public Point2D getCurrentPoint() throws IOException {
        return linePath.getCurrentPoint();
    }

    @Override
    public void closePath() throws IOException {
        linePath.closePath();
    }

    @Override
    public void endPath() throws IOException {
        if (clipWindingRule != -1) {
            linePath.setWindingRule(clipWindingRule);
            getGraphicsState().intersectClippingPath(linePath);
            clipWindingRule = -1;
        }
        linePath.reset();

    }

    @Override
    public void strokePath() throws IOException {
        rectList.add(linePath.getBounds2D());
        linePath.reset();
    }

    @Override
    public void fillPath(int windingRule) throws IOException {
        linePath.reset();
    }

    @Override
    public void fillAndStrokePath(int windingRule) throws IOException {
        linePath.reset();
    }

    @Override
    public void shadingFill(COSName cosn) throws IOException {
    }

    /**
     * This will print the usage for this document.
     */
    private static void usage() {
        System.err.println("Usage: java " + LineCatcher.class.getName() + " <input-pdf>" + " <output-file>");
    }
}
